#include "ogrebindingPCH.h"
#include "camerabinding.h"
#include "vector3binding.h"
#include "quaternionbinding.h"
#include "OgreViewPort.h"

namespace lua_ogre {

const char* CameraClassName = "Camera";

LuaCamera::LuaCamera(Ogre::Camera *cam) : c_obj(cam)
{
    cam->addListener(this);
}

LuaCamera::~LuaCamera()
{
	if (NULL == c_obj)
	{
		return;
	}

	c_obj->removeListener(this);
}

const char* LuaCamera::GetName() const
{
	if (NULL == c_obj)
	{
		return "Invalid Camera";
	}

	return c_obj->getName().c_str();
}

void LuaCamera::cameraDestroyed(Ogre::Camera*)
{
	if (NULL != c_obj)
	{
		c_obj->removeListener(this);
		c_obj = NULL;
	}
}
    
static LuaCamera* CreateCameraFromUserData(Ogre::Camera *cam, lua_State *ls)
{
    if (NULL == cam)
	{
		return 0;
	}
    
	LuaCamera *mem = (LuaCamera*)lua_newuserdata(ls, sizeof(LuaCamera));
	if (NULL == mem)
	{
		return 0;
	}
    new(mem) LuaCamera(cam);
	luaL_getmetatable(ls, CameraClassName);
	lua_setmetatable(ls, -2);
    
    return mem;
}
    
LuaCamera* CreateLuaCamera(Ogre::Camera *cam, lua_State *ls)
{
    LuaCamera *lua_cam = CreateCameraFromUserData(cam, ls);
    if (0 != lua_cam)
    {
        lua_pop(ls, 1);
    }
    
    return lua_cam;
}

int PushLuaCamera(Ogre::Camera *cam, lua_State *ls)
{
	LuaCamera *lua_cam = CreateCameraFromUserData(cam, ls);
    if (0 == lua_cam)
    {
        return 0;
    }
	return 1;
}

static int CameraAlloc(lua_State *ls)
{
	LogLuaError << "call scene::createcamera instead!";
	return 0;
}

static int SetBackgroundColor(Ogre::Camera *self, lua_State *ls)
{
	Ogre::Viewport *vp = self->getViewport();
	if (NULL == vp)
	{
		LogLuaError << "camera is not attach to any viewport!";
		return 0;
	}

	Ogre::ColourValue color;
	color.a = 1.0f;
	int arg_count = lua_gettop(ls);
	if (arg_count == 1 && lua_isnumber(ls, 1))
	{
		color.setAsARGB(Ogre::uint32(lua_tonumber(ls, 1)));
	}
	else
	{
		LogLuaError << "Camera:setbackgroundcolor(argb(int)/r,g,b,a(1.0)) failed! invalid parameters!";
		return 0;
	}

	vp->setBackgroundColour(color);
	return 0;
}

static int GetBackgroundColor(Ogre::Camera *self, lua_State *ls)
{
	Ogre::Viewport *vp = self->getViewport();
	if (NULL == vp)
	{
		LogLuaError << "camera is not attach to any viewport!";
		return 0;
	}

	const Ogre::ColourValue &color = vp->getBackgroundColour();
	char value[20];
	sprintf(value, "0x%x", color.getAsABGR());
	lua_pushlstring(ls, value, strlen(value));
	return 1;
}

static int GetPosition(Ogre::Camera *self, lua_State *ls)
{
	return LuaCreateVector3(self->getPosition(), ls);
}

static int SetPosition(Ogre::Camera *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, Vector3ClassName))
	{
		LogLuaError << "invalid value for camera.position!";
		return 0;
	}

	self->setPosition(*(Ogre::Vector3*)lua_touserdata(ls, 1));
	return 0;
}

static int GetOrient(Ogre::Camera *self, lua_State *ls)
{
	return LuaCreateQuaternion(self->getOrientation(), ls);
}

static int SetOrient(Ogre::Camera *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, QuaternionClassName))
	{
		LogLuaError << "invalid value for camera.orientation!";
		return 0;
	}
	self->setOrientation(*(Ogre::Quaternion*)lua_touserdata(ls, 1));
	return 0;
}

static int Rotate(Ogre::Camera *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !luaL_checkudata(ls, 1, QuaternionClassName))
	{
		LogLuaError << "Camera:Rotate() failed, invalid paramter!";
		return 0;
	}

	self->rotate(*(Ogre::Quaternion*)lua_touserdata(ls, 1));
	return 0;
}

static LuaMethod<Ogre::Camera> g_camera_methods_and_props[] =
{
	{"backgroundcolor", NULL, GetBackgroundColor, SetBackgroundColor},
	{"position", NULL, GetPosition, SetPosition},
	{"orientation", 0, GetOrient, SetOrient},
	{"rotate", Rotate, 0, 0},
	{0, 0, 0, 0}
};


void BindCamera(lua_State *ls)
{
	typedef LuaClassBind<Ogre::Camera, LuaCamera> CameraBind;

	LuaTypeObject<Ogre::Camera> type_obj(
		CameraClassName,
		g_camera_methods_and_props,
		CameraAlloc,
		CameraBind::Dealloc,
		CameraBind::ToString,
		CameraBind::Thunk);

	CameraBind::Register(ls, "ov", type_obj);
}

}